﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace SStuff.XnaInterop
{
    public partial class InteropModel
    {
        public InteropVertexBuffer[] VertexBuffers { get; set; }

        public InteropIndexBuffer[] IndexBuffers { get; set; }

        public InteropTexture2D[] Textures { get; set; }

        public InteropMesh[] Meshes { get; set; }

        public InteropBone[] Bones { get; set; }

        public InteropModel()
        { }


        public static InteropModel FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropModel();

            int vertexBuffersCount = reader.ReadInt32();
            result.VertexBuffers = new InteropVertexBuffer[vertexBuffersCount];
            for (int i = 0; i < vertexBuffersCount; i++)
            {
                result.VertexBuffers[i] = InteropVertexBuffer.FromBinaryReader(reader);
            }

            int indexBuffersCount = reader.ReadInt32();
            result.IndexBuffers = new InteropIndexBuffer[indexBuffersCount];
            for (int i = 0; i < indexBuffersCount; i++)
            {
                result.IndexBuffers[i] = InteropIndexBuffer.FromBinaryReader(reader);
            }

            int texturesCount = reader.ReadInt32();
            result.Textures = new InteropTexture2D[texturesCount];
            for (int i = 0; i < texturesCount; i++)
                result.Textures[i] = InteropTexture2D.FromBinaryReader(reader);

            int meshCount = reader.ReadInt32();
            result.Meshes = new InteropMesh[meshCount];
            for (int i = 0; i < meshCount; i++)
                result.Meshes[i] = InteropMesh.FromBinaryReader(reader);

            int boneCount = reader.ReadInt32();
            result.Bones = new InteropBone[boneCount];
            for (int i = 0; i < boneCount; i++)
                result.Bones[i] = InteropBone.FromBinaryReader(reader);

            return result;
        }

        public void Save(BinaryWriter writer)
        {
            if (VertexBuffers == null)
                VertexBuffers = new InteropVertexBuffer[] { };
            if (IndexBuffers == null)
                IndexBuffers = new InteropIndexBuffer[] { };
            if (Textures == null)
                Textures = new InteropTexture2D[] { };
            if (Meshes == null)
                Meshes = new InteropMesh[] { };
            if (Bones == null)
                Bones = new InteropBone[] { };

            writer.Write(VertexBuffers.Length);
            for (int i = 0; i < VertexBuffers.Length; i++)
                VertexBuffers[i].Save(writer);

            writer.Write(IndexBuffers.Length);
            for (int i = 0; i < IndexBuffers.Length; i++)
                IndexBuffers[i].Save(writer);

            writer.Write(Textures.Length);
            for (int i = 0; i < Textures.Length; i++)
                Textures[i].Save(writer);

            writer.Write(Meshes.Length);
            for (int i = 0; i < Meshes.Length; i++)
                Meshes[i].Save(writer);

            writer.Write(Bones.Length);
            for (int i = 0; i < Bones.Length; i++)
            {
                Bones[i].Save(writer);
            }
        }
    }

    public partial class InteropVertexBuffer
    {

        public int VertexStride { get; set; }
        public int VertexCount { get; set; }
        public InteropVertexElement[] Elements { get; set; }
        public byte[] Data { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(VertexStride);
            writer.Write(VertexCount);
            writer.Write(Elements.Length);
            for (int i = 0; i < Elements.Length; i++)
            {
                Elements[i].Save(writer);
            }
            writer.Write(Data.Length);
            writer.Write(Data);
        }

        public static InteropVertexBuffer FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropVertexBuffer();
            result.VertexStride = reader.ReadInt32();
            result.VertexCount = reader.ReadInt32();
            int elementsCount = reader.ReadInt32();
            result.Elements = new InteropVertexElement[elementsCount];
            for (int i = 0; i < elementsCount; i++)
            {
                result.Elements[i] = InteropVertexElement.FromBinaryReader(reader);
            }
            int dataLength = reader.ReadInt32();
            result.Data = reader.ReadBytes(dataLength);
            return result;
        }
    }

    public partial class InteropIndexBuffer
    {
        public int IndexCount { get; set; }
        public bool Is32BitFormat { get; set; }
        public int[] DataInt32 { get; set; }
        public ushort[] DataInt16 { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(IndexCount);
            writer.Write(Is32BitFormat);
            if (Is32BitFormat)
            {
                byte[] data = new byte[4 * IndexCount];
                Buffer.BlockCopy(DataInt32, 0, data, 0, 4 * IndexCount);
                writer.Write(data);
            }
            else
            {
                byte[] data = new byte[2 * IndexCount];
                Buffer.BlockCopy(DataInt16, 0, data, 0, 2 * IndexCount);
                writer.Write(data);
            }
        }

        public static InteropIndexBuffer FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropIndexBuffer();
            result.IndexCount = reader.ReadInt32();
            result.Is32BitFormat = reader.ReadBoolean();
            if (result.Is32BitFormat)
            {
                var buffer = reader.ReadBytes(result.IndexCount * 4);
                result.DataInt32 = new int[result.IndexCount];
                Buffer.BlockCopy(buffer, 0, result.DataInt32, 0, buffer.Length);
            }
            else
            {
                var buffer = reader.ReadBytes(result.IndexCount * 2);
                result.DataInt16 = new ushort[result.IndexCount];
                Buffer.BlockCopy(buffer, 0, result.DataInt16, 0, buffer.Length);

            }
            return result;
        }
    }

    public partial class InteropVertexElement
    {
        public int Offset { get; set; }
        public int UsageIndex { get; set; }
        public string Format { get; set; }
        public string Usage { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(Offset);
            writer.Write(UsageIndex);
            writer.Write(Format);
            writer.Write(Usage);
        }

        public static InteropVertexElement FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropVertexElement();
            result.Offset = reader.ReadInt32();
            result.UsageIndex = reader.ReadInt32();
            result.Format = reader.ReadString();
            result.Usage = reader.ReadString();
            return result;
        }
    }

    public partial class InteropMesh
    {
        public string Name { get; set; }
        public InteropMeshPart[] Parts { get; set; }
        public float BoundingSphereCenterX { get; set; }
        public float BoundingSphereCenterY { get; set; }
        public float BoundingSphereCenterZ { get; set; }
        public float BoundingSphereRadius { get; set; }

        public int BoneIndex { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(Name);
            writer.Write(BoundingSphereCenterX);
            writer.Write(BoundingSphereCenterY);
            writer.Write(BoundingSphereCenterZ);
            writer.Write(BoundingSphereRadius);
            writer.Write(BoneIndex);

            writer.Write(Parts.Length);

            for (int i = 0; i < Parts.Length; i++)
                Parts[i].Save(writer);
        }

        public static InteropMesh FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropMesh();
            result.Name = reader.ReadString();
            result.BoundingSphereCenterX = reader.ReadSingle();
            result.BoundingSphereCenterY = reader.ReadSingle();
            result.BoundingSphereCenterZ = reader.ReadSingle();
            result.BoundingSphereRadius = reader.ReadSingle();
            result.BoneIndex = reader.ReadInt32();
            var partLength = reader.ReadInt32();
            result.Parts = new InteropMeshPart[partLength];
            for (int i = 0; i < partLength; i++)
            {
                result.Parts[i] = InteropMeshPart.FromBinaryReader(reader);
            }
            return result;
        }
    }

    public partial class InteropTexture2D
    {
        public int Width { get; set; }
        public int Height { get; set; }
        public byte[] PngData { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(Width);
            writer.Write(Height);
            writer.Write(PngData.Length);
            writer.Write(PngData);
        }

        public static InteropTexture2D FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropTexture2D();
            result.Width = reader.ReadInt32();
            result.Height = reader.ReadInt32();
            var pngLength = reader.ReadInt32();
            result.PngData = reader.ReadBytes(pngLength);
            return result;
        }
    }


    public partial class InteropTextureUsage
    {
        public string EffectParameterName { get; set; }
        public int TextureIndex { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(EffectParameterName);
            writer.Write(TextureIndex);
        }

        public static InteropTextureUsage FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropTextureUsage();
            result.EffectParameterName = reader.ReadString();
            result.TextureIndex = reader.ReadInt32();
            return result;
        }
    }

    public partial class InteropEffectParameter
    {
        public string ParameterName { get; set; }
        public Vector4[] Data { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(ParameterName);
            if (Data == null)
            {
                writer.Write(0);
            }
            else
            {
                writer.Write(Data.Length);
                foreach (var val in Data)
                {
                    writer.Write(val.X);
                    writer.Write(val.Y);
                    writer.Write(val.Z);
                    writer.Write(val.W);
                }
            }
        }

        public static InteropEffectParameter FromBinaryReader(BinaryReader reader)
        {
            var res = new InteropEffectParameter();
            res.ParameterName = reader.ReadString();
            int count = reader.ReadInt32();
            if (count > 0)
            {
                res.Data = new Vector4[count];
                for (int i = 0; i < count; i++)
                {
                    res.Data[i] = new Vector4();
                    res.Data[i].X = reader.ReadSingle();
                    res.Data[i].Y = reader.ReadSingle();
                    res.Data[i].Z = reader.ReadSingle();
                    res.Data[i].W = reader.ReadSingle();
                }
            }
            return res;
        }
    }

    public partial class InteropMeshPart
    {
        public int VertexBufferIndex { get; set; }
        public int IndexBufferIndex { get; set; }
        public int VertexOffset { get; set; }
        public int NumVertices { get; set; }
        public int PrimitiveCount { get; set; }
        public int StartIndex { get; set; }
        public string EffectName { get; set; }
        public string EffectTechnique { get; set; }

        public InteropTextureUsage[] TextureUsages { get; set; }
        public InteropEffectParameter[] EffectParameters { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(VertexBufferIndex);
            writer.Write(IndexBufferIndex);
            writer.Write(VertexOffset);
            writer.Write(NumVertices);
            writer.Write(PrimitiveCount);
            writer.Write(StartIndex);
            writer.Write(EffectName);
            writer.Write(EffectTechnique);
            writer.Write(TextureUsages.Length);
            for (int i = 0; i < TextureUsages.Length; i++)
                TextureUsages[i].Save(writer);
            writer.Write(EffectParameters.Length);
            for (int i = 0; i < EffectParameters.Length; i++)
                EffectParameters[i].Save(writer);
        }

        public static InteropMeshPart FromBinaryReader(BinaryReader reader)
        {
            var result = new InteropMeshPart();
            result.VertexBufferIndex = reader.ReadInt32();
            result.IndexBufferIndex = reader.ReadInt32();
            result.VertexOffset = reader.ReadInt32();
            result.NumVertices = reader.ReadInt32();
            result.PrimitiveCount = reader.ReadInt32();
            result.StartIndex = reader.ReadInt32();
            result.EffectName = reader.ReadString();
            result.EffectTechnique = reader.ReadString();
            int texUsages = reader.ReadInt32();
            result.TextureUsages = new InteropTextureUsage[texUsages];
            for (int i = 0; i < texUsages; i++)
            {
                result.TextureUsages[i] = InteropTextureUsage.FromBinaryReader(reader);
            }
            int effectParams = reader.ReadInt32();
            result.EffectParameters = new InteropEffectParameter[effectParams];
            for (int i = 0; i < effectParams; i++)
            {
                result.EffectParameters[i] = InteropEffectParameter.FromBinaryReader(reader);
            }
            return result;
        }
    }

    public partial class InteropBone
    {
        public string Name { get; set; }
        public bool IsRoot { get; set; }
        public int ParentIndex { get; set; }
        public int Index { get; set; }
        public Matrix Transform { get; set; }

        public void Save(BinaryWriter writer)
        {
            writer.Write(Name ?? "");
            writer.Write(IsRoot);
            writer.Write(ParentIndex);
            writer.Write(Index);
            writer.Write(Transform.M11);
            writer.Write(Transform.M12);
            writer.Write(Transform.M13);
            writer.Write(Transform.M14);
            writer.Write(Transform.M21);
            writer.Write(Transform.M22);
            writer.Write(Transform.M23);
            writer.Write(Transform.M24);
            writer.Write(Transform.M31);
            writer.Write(Transform.M32);
            writer.Write(Transform.M33);
            writer.Write(Transform.M34);
            writer.Write(Transform.M41);
            writer.Write(Transform.M42);
            writer.Write(Transform.M43);
            writer.Write(Transform.M44);
        }

        public static InteropBone FromBinaryReader(BinaryReader reader)
        {
            var bone = new InteropBone();
            bone.Name = reader.ReadString();
            bone.IsRoot = reader.ReadBoolean();
            bone.ParentIndex = reader.ReadInt32();
            bone.Index = reader.ReadInt32();
            bone.Transform = new Matrix(
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle(),
                reader.ReadSingle()
                );
            return bone;
        }
    }
}
